# 第02章 MyBatis初始化过程

# 2.1 初始化过程做了什么?

MyBatis的初始化可以有两种方式:

  • 基于XML配置文件:基于XML配置文件的方式是将MyBatis的所有配置信息放在XML文件中,MyBatis通过加载并XML配置文件,将配置文信息组装成内部的Configuration对象
  • 基于Java API:这种方式不使用XML配置文件,需要MyBatis使用者在Java代码中,手动创建Configuration对象,然后将配置参数set 进入Configuration对象中

本章源码分析是基于XML配置文件方式的MyBatis初始化

String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

SqlSession sqlSession = sqlSessionFactory.openSession();
try {
    OtaPackageMapper otaPackageMapper = sqlSession.getMapper(OtaPackageMapper.class);
    List<OtaPackage> otaPackages = otaPackageMapper.selectOtaPackageList();
    for (OtaPackage otaPackage : otaPackages) {
        System.out.println("packageId:"+otaPackage.getPackageId());
    }
}catch (Exception e){
    e.printStackTrace();
}finally {
    sqlSession.close();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

上述语句的作用是执行com.clare.mapper.OtaPackageMapper.selectOtaPackageList 定义的SQL语句,返回一个List结果集。总的来说,上述代码经历了mybatis初始化 -->创建SqlSession -->执行SQL语句 返回结果三个过程。

上述代码的功能是根据配置文件mybatis-config.xml 配置文件,创建SqlSessionFactory对象,然后产生SqlSession,执行SQL语句。而mybatis的初始化就发生在第三句:SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

根据1.1.6节并结合源码分析,MyBatis基本的初始化过程如下(省略掉部分细节):

1550243071111

简单分析过程如下:

  • 调用SqlSessionFactoryBuilder对象的build(inputStream)方法;
  • SqlSessionFactoryBuilder会根据输入流inputStream等信息创建XMLConfigBuilder对象;
  • SqlSessionFactoryBuilder调用XMLConfigBuilder对象的parse()方法;
  • XMLConfigBuilder对象返回Configuration对象;
  • SqlSessionFactoryBuilder根据Configuration对象创建一个DefaultSessionFactory对象;
  • SqlSessionFactoryBuilder返回 DefaultSessionFactory对象给Client,供Client使用。

MyBatis初始化基本过程:

SqlSessionFactoryBuilder根据传入的数据流生成Configuration对象,然后根据Configuration对象创建默认的SqlSessionFactory实例。

首先,给文件mybatis-config.xml创建输入流:

String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
1
2
3

或者使用Reader:

String resource = "mybatis-config.xml";
Reader resourceAsReader = Resources.getResourceAsReader(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsReader);
1
2
3

底层的代码实现:

public static InputStream getResourceAsStream(String resource) throws IOException {
    return getResourceAsStream(null, resource);
}

public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
    InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader);
    if (in == null) {
      throw new IOException("Could not find resource " + resource);
    }
    return in;
}

public static Reader getResourceAsReader(String resource) throws IOException {
    Reader reader;
    if (charset == null) {
      reader = new InputStreamReader(getResourceAsStream(resource));
    } else {
      reader = new InputStreamReader(getResourceAsStream(resource), charset);
    }
    return reader;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

就是将输入的路径转换为一个输入流并返回

SqlSessionFactoryBuilder相关代码如下:

  public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, null, null);
  }

  public SqlSessionFactory build(InputStream inputStream, 
                                 String environment, 
                                 Properties properties) {
    try {
      // 创建XMLConfigBuilder对象用来解析XML配置文件,生成Configuration对象  
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, 
                                                     environment, 
                                                     properties);
      // 将XML配置文件内的信息解析成Java对象Configuration对象
      Configuration config = parser.parse();
      // 根据Configuration对象创建出SqlSessionFactory对象
      return build(config);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        inputStream.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }
  
  // 从此处可以看出,MyBatis内部通过Configuration对象来创建SqlSessionFactory
  // 用户也可以自己通过API构造好Configuration对象,调用此方法创建SqlSessionFactory  
  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

  // Reader模式实现
  public SqlSessionFactory build(Reader reader) {
    return build(reader, null, null);
  }

  public SqlSessionFactory build(Reader reader, 
                                 String environment, 
                                 Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        reader.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

上述的初始化过程中,涉及到了以下几个对象:

  • SqlSessionFactoryBuilder : SqlSessionFactory的构造器,用于创建SqlSessionFactory,采用了Builder设计模式(第02章介绍)
  • Configuration :该对象是mybatis-config.xml文件中所有mybatis配置信息
  • SqlSessionFactory:SqlSession工厂类,以工厂形式创建SqlSession对象,采用了Factory工厂设计模式(第03章介绍)
  • XmlConfigParser :负责将mybatis-config.xml配置文件解析成Configuration对象,供SqlSessonFactoryBuilder使用,创建SqlSessionFactory

# 2.2 Configuration对象创建

任何框架的初始化,无非是加载自己运行时所需要的配置信息。MyBatis的配置信息,大概包含以下信息,其高层级结构如下:

× configuration 配置

​ × properties 属性 ​ × settings 设置 ×typeAliases 类型命名 ×typeHandlers 类型处理器 ×objectFactory 对象工厂 ×plugins 插件 ×environments 环境 ​ ×environment 环境变量 ​ ×transactionManager 事务管理器 ​ ×dataSource 数据源 ×映射器

MyBatis的上述配置信息会配置在XML配置文件中,那么,这些信息被加载进入MyBatis内部,MyBatis是怎样维护的呢?

MyBatis采用了一个非常直白和简单的方式---使用org.apache.ibatis.session.Configuration 对象作为一个所有配置信息的容器,Configuration对象的组织结构和XML配置文件的组织结构几乎完全一样(当然,Configuration对象的功能并不限于此,它还负责创建一些MyBatis内部使用的对象,如Executor等,这将在后文中讨论)。如下图所示:

1550304927390

MyBatis根据初始化好Configuration信息,这时候用户就可以使用MyBatis进行数据库操作了。

可以这么说,MyBatis初始化的过程,就是创建 Configuration对象的过程。

接着上述的 MyBatis初始化基本过程讨论,当SqlSessionFactoryBuilder执行build()方法,调用了XMLConfigBuilder的parse()方法,然后返回了Configuration对象。那么parse()方法是如何处理XML文件,生成Configuration对象的呢?

SqlSessionFactoryBuilder:

1550306576480

分析SqlSessionFactoryBuilder执行语句(省去异常捕获):

  public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, null, null);
  }

  public SqlSessionFactory build(InputStream inputStream, 
                                 String environment, 
                                 Properties properties) {
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, 
                                                     environment, 
                                                     properties);
      Configuration config = parser.parse();
      return build(config);
    }
  }
  
  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

build中会根据inputStream流创建XMLConfigBuilder对象

public XMLConfigBuilder(InputStream inputStream, 
                        String environment, 
                        Properties props) {
    this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), 
         environment, 
         props);
}

private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

上述过程新建了XMLMapperEntityResolver对象,并根据该对象创建了XPathParser对象,然后调用了私有的构造方法,首先调用了父类BaseBuilder的构造器(XMLConfigBuilder内部含XPathParser对象,BaseBuilder内部含Configuration对象)

1550306106786

1550306664888

1550306147617

public abstract class BaseBuilder {
  protected final Configuration configuration;
  protected final TypeAliasRegistry typeAliasRegistry;
  protected final TypeHandlerRegistry typeHandlerRegistry;

  public BaseBuilder(Configuration configuration) {
    this.configuration = configuration;
    this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
    this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
  }
    
  // ... 
}
1
2
3
4
5
6
7
8
9
10
11
12
13

可见新建了Configuration内部对象,而new Configuration()如下:

public Configuration() {
    typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
    typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);

    typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
    typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
    typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);

    typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
    typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
    typeAliasRegistry.registerAlias("LRU", LruCache.class);
    typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
    typeAliasRegistry.registerAlias("WEAK", WeakCache.class);

    typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);

    typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
    typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);

    typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
    typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
    typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
    typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
    typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
    typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
    typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);

    typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
    typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);

    languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
    languageRegistry.register(RawLanguageDriver.class);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

可见使用类型别名注册器和语言注册器注册了一系列的类的别名和language,在mybatis-config.xml中也可以使用别名标签,最终也会调用别名注册器进行注册:

<typeAliases>
    <!--自定义定义单个类的别名-->
    <typeAlias type="com.clare.model.OtaPackage" alias="otaPackage"/>
    <!--通过自动扫描形式自定义别名-->
    <package name="com.clare.model"/>
</typeAliases>
1
2
3
4
5
6

关于类型注册器TypeAliasRegistry下章讲解,主要是往Map中存储key为别名,value为类的全限定路径

创建好了XMLConfigBuilder后,调用其的parse()方法创建Configuration对象:

 public Configuration parse() {
     if (parsed) {
         throw new BuilderException("Each XMLConfigBuilder can only be used once.");
     }
     parsed = true;
     parseConfiguration(parser.evalNode("/configuration"));
     return configuration;
 }

private void parseConfiguration(XNode root) {
    try {
        //issue #117 read properties first
        propertiesElement(root.evalNode("properties"));
        Properties settings = settingsAsProperties(root.evalNode("settings"));
        loadCustomVfs(settings);
        loadCustomLogImpl(settings);
        typeAliasesElement(root.evalNode("typeAliases"));
        pluginElement(root.evalNode("plugins"));
        objectFactoryElement(root.evalNode("objectFactory"));
        objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
        reflectorFactoryElement(root.evalNode("reflectorFactory"));
        settingsElement(settings);
        // read it after objectFactory and objectWrapperFactory issue #631
        environmentsElement(root.evalNode("environments"));
        databaseIdProviderElement(root.evalNode("databaseIdProvider"));
        typeHandlerElement(root.evalNode("typeHandlers"));
        mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
        throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

方法parser.evalNode("/configuration")调用了XPathParser的evalNode(String expression)的方法,用于解析节点。parseConfiguration(XNode root)用于解析XNode设置到Configuration中。最终返回Configuration对象。(随后介绍XPathParser的创建及如何解析XML文件)

最后,SqlSessionFactoryBuilder根据Configuration对象创建默认的SqlSessionFactory返回。

Last Updated: 10/20/2019, 11:49:45 PM